<?php

/**
 * @Author: king
 * @Date:   2019-05-02 16:01:29
 * @Last Modified by:   king
 * @Last Modified time: 2019-06-01 22:07:04
 */

namespace App\Tools;

use App\Exceptions\ApiException;
use App\Models\Api\Orders as OrdersModel;
use App\Models\Api\Recode;
use App\Models\Api\LanmeiOrderRefund;
use App\Models\Api\Rectification as RectificationModel;
use App\Http\Controllers\Api\v1\Pay\AliController;
use App\Http\Controllers\Api\v1\Pay\WeixinController;
use App\Http\Controllers\Api\v1\Pay\JdController;


/**
 * 订单拓展类库
 * Class Orders
 * @package App\Tools
 */
class Orders
{

    /**
     * 统一三方下单入口
     * @param array $map
     * @return array|bool
     * @throws \Exception
     */
    public function unifyPayOrder($map = [])
    {
        $trade_type = $map['trade_type'];
        $pay_des = $map['pay_des'];
        $total_amount = $map['total_amount'];
        $order_id = $map['order_id'];
        $gid =  $map['gid'] ?? 0;
        $openid = $map['openid'];
        $time = time();
        $callback_url = $map['callback_url'];
        $order_type = $map['order_type'];
        $arr = [
            'status' => false,
            'data' => []
        ];
        //        支付订单
        switch (true)
        {
            // 支付宝APP支付 支付类型为 APP
            case $trade_type == 1:
                $total_amount = ($total_amount);// 以元为单位的
                $AliPay = new AliController();
                $pay_url = $AliPay->pay($pay_des, $order_id, $total_amount, 'APP');
                $arr = [
                    'status' => true,
                    'data' => [
                        'pay_url' => $pay_url,
                        'order_id' => $order_id
                    ],
                ];
                break;
            // 微信支付 2 APP支付  6 JSAPI支付 8 H5
            case in_array($trade_type, [2, 6, 8]):
                $total_amount = sprintf("%u", $total_amount * 100);// 以分为单位的
                $WxPay = new WeixinController();
                // 获取支付类型 APP、JSAPI 或 H5
                $wxpayConfig = self::getPayConfig('wx');
               
                if (empty($wxpayConfig))
                {
                    return $arr;
                }

                $wxPayType = $wxpayConfig['wx_pay_type'][$trade_type];
                // 获取微信配置信息
                $wxConfig = $wxpayConfig[$wxPayType['configName']];

                $data = $WxPay->pay($pay_des, $order_id, $total_amount, $wxPayType['type'], $wxConfig, $openid);
                // 下单成功
                if ($data['return_code'] === 'SUCCESS' && $data['result_code'] === 'SUCCESS')
                {
                    switch ($trade_type)
                    {
                        // APP支付
                        case 2:
                            $sign['appid'] = $wxConfig['appid'];
                            $sign['partnerid'] = $wxConfig['mchid'];
                            $sign['prepayid'] = $data['prepay_id'];
                            $sign['package'] = 'Sign=WXPay';
                            $sign['noncestr'] = $WxPay->getRandChar(32);
                            $sign['timestamp'] = $time;
                            $sign['sign'] = $WxPay->getSign($sign, $wxConfig);
                            $sign['order_id'] = $order_id;

                            $data['nonce_str'] = $sign['noncestr'];
                            $data['sign'] = $sign['sign'];

                            $arr = [
                                'status' => true,
                                'data' => $sign,
                            ];
                            break;
                        // JSAPI支付
                        case 6:
                            $sign = [
                                'appId' => $wxConfig['appid'],
                                'timeStamp' => $time,
                                'nonceStr' => $WxPay->getRandChar(32),
                                'package' => 'prepay_id=' . $data['prepay_id'],
                                'signType' => 'MD5',
                            ];
                            $sign['paySign'] = $WxPay->getSignOfJsApi($sign, $wxConfig);

                            $sign['order_id'] = $order_id;
                            $arr = [
                                'status' => true,
                                'data' => $sign,
                            ];
                            break;
                        // H5支付返回
                        case 8:
                            $sign = [
                                'mweb_url' => ($data['mweb_url'] . '&redirect_url=' . ($order_type == 1 ? $callback_url : urlencode(urldecode($callback_url) . $order_id))),
                                'order_id' => $order_id
                            ];

                            $arr = [
                                'status' => true,
                                'data' => $sign,
                            ];
                            break;
                        default:
                            break;
                    }
                } else
                {
                    $ret = [
                        'return_code' => $data['return_code'],
                        'return_msg' => $data['return_msg'],
                    ];
                    $arr = [
                        'status' => false,
                        'data' => $ret,
                    ];
                }
                break;
            // 京东支付
            case $trade_type == 5:
                $total_amount = sprintf("%u", $total_amount * 100);// 以分为单位的
                $JdPay = new JdController();
                $data = $JdPay->pay($pay_des, $order_id, $total_amount);

                if ($data === false)
                {
                    $arr = [
                        'status' => false,
                        'data' => $data
                    ];
                } else
                {
                    $arr = [
                        'status' => true,
                        'data' => $data
                    ];
                }

                break;
            // 支付宝手机网页支付 支付类型为 WAP
            case $trade_type == 7:
                $total_amount = ($total_amount);// 以元为单位的
                $AliPay = new AliController();
                $payForm = $AliPay->pay($pay_des, $order_id, $total_amount, 'WAP', $order_type == 1 ? $callback_url : urlencode(urldecode($callback_url) . $order_id));
                $arr = [
                    'status' => true,
                    'data' => [
                        'par_form' => $payForm,
                        'order_id' => $order_id
                    ],
                ];
                break;
            case $trade_type == 9:
                         $arr = [
                            'status' => true,
                             'data' => [
                             'order_id' => $order_id,
                                ],
                            ];
                    if ($gid){
                             $skuId = 'pay_fortune_' . $gid;
                             $arr['data']['skuId'] = $skuId;
                          }
                            break;
                        default :
                            $arr = [
                                'status' => false,
                                'data' => [],
                            ];
                            break;
        }

        return $arr;
    }

    /**
     * 验证订单是否已经支付
     * @param array $map
     * @return bool|string
     * @throws ApiException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function validate_orders($map = [])
    {
        $uid = $map['uid'];
        $is_pay_again = $map['is_pay_again'] ?? false;
        $order_id = $map['order_id'];
        $num = substr($order_id, 1, 1);

        $arr[] = $order_id;

        //获取当前订单信息
        $OrdersModel = new OrdersModel();
        $order = $OrdersModel->get_orders_detail(['order_type' => $num, 'order_ids' => $arr, 'uid' => $uid]);
        if ($order) {
            $order = $order[0];
        }

        if(!empty($order))
        {
            // 若该订单已支付，则直接返回已支付
            if ($order['status'] == 1 && $is_pay_again == true)
            {
                if ($order['trade_type'] == 1)
                {
                    return '订单已在支付宝支付';
                } elseif ($order['trade_type'] == 2)
                {
                    return '订单已在微信支付';
                } else
                {
                    return '订单已支付';
                }
            }

            $orderAccount = isset($order['account']) ? intval($order['account']) : 1;

            // 订单支付方式为支付宝
            if ($order['trade_type'] == 1)
            {
                $ali = new AliController();

                $response = json_decode(json_encode($ali->query(['out_trade_no' => $order['order_id']], $orderAccount)), true);

                // 支付宝已经内置验签 支付成功
                if ($response['alipay_trade_query_response']['code'] == '10000')
                {
                    $tarde_status = $response['alipay_trade_query_response']['trade_status'];

                    if ($tarde_status == 'TRADE_SUCCESS')
                    {
                        if($order['status'] == 0)
                        {
                            //若支付宝已支付且订单状态为待支付进行修改及加入队列
                            if (isset($order['reid']) && $order['reid'] > 0)
                            {
                                $status = $this->updateAndPushRedis(['oid' => $order['id'], 'order_id' => $order['order_id'], 'id' => $order['reid']]);
                            } elseif (isset($order['fid']) && (int)$order['fid'] > 0)
                            {
                                $status = $this->updateAndPushFortune(['fid' => $order['fid'], 'uid' => $order['uid'], 'oid' => $order['oid'], 'update' => true]);
                            } else
                            {
                                $status = $this->updateOrders($order['order_id']);
                            }
                            if ($status !== true)
                            {
                                return '操作失败,请稍后重试';
                            }
                            // 订单状态成功修改后,删除订单缓存
                            (new OrdersModel())->rm_cache(['type' => 2, 'order_id' => $order['order_id'], 'uid' => $order['uid'], 'status' => $status]);
                        }

                        if ($is_pay_again == true)
                        {
                            return '订单已在支付宝支付';
                        }
                    }
                }
            } // 订单支付方式为微信支付
            elseif (in_array($order['trade_type'], [2, 6, 8]))
            {
                $weixin = new WeixinController();
                $wxpayConfig = self::getPayConfig('wx', $orderAccount);

                if (empty($wxpayConfig))
                {
                    return '操作失败,请稍后重试';
                }

                $wxPayType = $wxpayConfig['wx_pay_type'][$order['trade_type']];
                // 获取微信配置信息
                $wxConfig = $wxpayConfig[$wxPayType['configName']];

                $response = $weixin->query($order['order_id'], $wxConfig);
                // 验签
                if ((new PayCheckSign())->weixin($response, $wxConfig['api_key']))
                {
                    if ($response['return_code'] === 'SUCCESS')
                    {
                        if ($response['result_code'] === 'SUCCESS' && $response['trade_state'] == 'SUCCESS')
                        {
                            if($order['status'] == 0)
                            {
                                //若微信已支付且订单状态为待支付进行修改及加入队列
                                if (isset($order['reid']) && $order['reid'] > 0)
                                {
                                    $status = $this->updateAndPushRedis(['id' => $order['reid'], 'oid' => $order['oid'], 'order_id' => $order['order_id']]);
                                } elseif (isset($order['fid']) && intval($order['fid']) > 0)
                                {
                                    $status = $this->updateAndPushFortune(['fid' => $order['fid'], 'uid' => $order['uid'], 'oid' => $order['oid'], 'update' => true]);
                                } else
                                {
                                    $status = $this->updateOrders($order['order_id']);
                                }
                                if ($status !== true)
                                {
                                    return '操作失败,请稍后重试';
                                }

                                // 订单状态成功修改后,删除订单缓存
                                (new OrdersModel())->rm_cache(['type' => 2, 'order_id' => $order['order_id'], 'uid' => $order['uid'], 'status' => $status]);
                            }
                            if ($is_pay_again == true)
                            {
                                return '订单已在微信支付';
                            }
                        }
                    } else
                    {
                        return '网络异常';
                    }
                } else
                {
                    return '网络异常';
                }
            }
            // TODO  订单支付方式为京东支付

            if ($order['status'] == 2 && $is_pay_again == true)
            {
                return '订单已关闭';
            }
        }


        return true;
    }

    /**
     * 更新订单状态为支付成功并添加任务到队列
     * @param array $map
     * @param bool $update
     * @return bool|string
     * @throws ApiException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    protected function updateAndPushRedis($map = [], $update = true)
    {
        $reid = $map['id'];
        $oid = $map['oid'];
        $order_id = $map['order_id'];

        $OrdersModel = new OrdersModel();
        $RecodeModel = new Recode();

        // 更新数据库状态
        if ($update === true)
        {
            try
            {
                // 支付成功 更新未支付的订单的支付状态
                $OrdersModel->where(['status' => 0, 'reid' => $reid])->update(['status' => 1, 'pay_time' => time()]);

                RectificationModel::update(['status' => 1],['id' => $reid, 'status' => 0]);
            }
            catch (\Exception $e)
            {
                return $e->getMessage();
            }
        }

        $rectification = RectificationModel::where(['id' => $reid])->first();
        $event_array   = $rectification->events()->get()->toArray();

        $recode = $RecodeModel->getRecode(['id' => $rectification['rid']]);


        $data = [
            'oid' => $oid,
            'order_id' => $order_id,
            'reid' => $reid,
            'start_time' => $rectification['start_time'],
            'end_time' => $rectification['end_time'],
            'events' => $event_array,
            'recode' => $recode
        ];
        $queueConfig = config('queue');
        queuePush($queueConfig['rectification_task'], $data, $queueConfig['queue'], $queueConfig['rectification_delay']);

//        rabbitTask('rectification',$data);

        return 2;
    }

    /**
     * 更新订单状态为支付成功并添加任务到队列
     * @param array $map
     * @return bool
     * @throws ApiException
     */
    protected function updateAndPushFortune($map = [])
    {
        $fid = $map['fid'];
        $update = $map['update'];

        $OrdersModel = new OrdersModel();

        // 更新数据库状态
        if ($update === true)
        {
            try
            {
                // 支付成功 更新未支付的订单的支付状态
                $OrdersModel->where(['status' => 0, 'fid' => $fid])->update(['status' => 1, 'pay_time' => time()]);
            }
            catch (\Exception $e)
            {
                return $e->getMessage();
            }
        }

        $uid = $map['uid'];
        $oid = $map['oid'];

        $where = ['o.id' => $oid];

        try
        {
            $fortune = (new OrdersModel())
                ->from('orders as o')
                ->select('f.time','f.type','f.rids','g.expire_time','o.status','o.order_id')
                ->join('goods as g', 'o.gid','=','g.id', 'INNER')
                ->join('fortune as f', 'o.fid','=','f.id', 'INNER')
                ->where($where)
                ->whereIn('o.gid',config('app.month_year_id'))
                ->firstOrFail()->toArray();
        }
        catch (\Exception $e)
        {
            $fortune = [];
        }

        if ($fortune && !empty($fortune))
        {
            $rids = explode(',', $fortune['rids']);

            $start_time = $fortune['time'];
            $end_time = strtotime($fortune['expire_time'], $fortune['time']);

            $recode_info = [];
            foreach ($rids as $rid)
            {
                //获取档案信息
                $recode_info[] = (new Recode())->getRecode(['uid' => $uid, 'id' => $rid]);
            }
            switch ($fortune['type'])
            {
                case 2:
                    $start_time = date('Y-m', $start_time);
                    $end_time = date('Y-m', strtotime('-1 months', $end_time));
                    break;
                case 3:
                    $start_time = date('Y', $start_time);
                    $end_time = date('Y', strtotime('-1 years', $end_time));
                    break;
                default:
                    $start_time = "";
                    $end_time = "";
            }
            $unlock_time = prDates(['start' => $start_time, 'end' => $end_time, 'type' => $fortune['type']]);
            //将运势加入队列运算
            fortune_redis_job(['oid'=>$oid,'order_id' => $fortune['order_id'], 'recode_info' => $recode_info, 'unlock_time' => $unlock_time, 'ostatus' => $fortune['status'], 'ftype' => $fortune['type']]);
        }

        return 2;
    }

    /**
     * 更新订单状态为支付成功
     * @param $order_id
     * @return bool|string
     */
    protected function updateOrders($order_id)
    {
        $OrdersModel = new OrdersModel();

        $where = ['order_id' => $order_id, 'status' => 0];
        // 更新数据库状态
        try
        {
            // 支付成功 更新未支付的订单的支付状态
            $OrdersModel->where($where)->update(['status' => 1, 'pay_time' => time()]);
        }
        catch (\Exception $e)
        {
            return $e->getMessage();
        }

        return 3;
    }

    /**
     * 获取支付参数
     * 未获取到相关配置时，返回空数组
     * @param string $type 支付类型 ali=>支付宝 wx=>微信支付 jd=>京东支付
     * @param int $account 需要强制获取非使用中的配置，目前主要用于进行订单查询 （下单时不需要传参）
     * @return array
     */
    public static function getPayConfig($type = 'ali', $account = 0)
    {
        $type = strtolower($type) . 'pay';

        $config = config($type);

        if (empty($config))
        {
            return [];
        }

        if ($account > 0 && isset($config['account' . $account]))
        {
            $data = $config['account' . $account];
        } else
        {
            $data = $config['account' . $config['default']];
        }

        return $data;
    }

    /**
     * 统一退款
     * @param array $map
     *      id   退款ID
     *      refund_id   退款编号
     *      price   退款金额 元
     *      order_id   订单ID
     *      aid     操作员ID 队列结算 退款ID为0
     *      refund_reason 退款原因
     *      trade_type 支付方式
     * @return bool
     * @throws ApiException
     * @throws \Exception
     */
    public static function orderRefund(array $map)
    {
        $account = getAccountId($map['trade_type']);
        $map['price'] = sprintf("%.2f", $map['price']);
        $time = time();
        switch (true)
        {
            case in_array($map['trade_type'], [1, 7]):
                // 支付宝
                $data = [
                    'out_trade_no' => $map['order_id'],
                    'refund_amount' => $map['price'],
                    'refund_reason' => $map['refund_reason'],
                    'operator_id' => $map['aid'],
                    'out_request_no' => $map['refund_id'],
                ];
                $result = (new AliController())->refund($data, $account);
                break;
            case in_array($map['trade_type'], [2, 6, 8]):
                // 微信
                // $data = [
                //
                // ];
                // $result = (new Weixin())->refund($data);
                break;
            case $map['trade_type'] == 5:
                // 京东
                // $data = [
                //
                // ];
                // $result = true;

                break;
            case $map['trade_type'] == 9:
                // applePay
                // $data = [
                //
                // ];
                // $result = true;
                break;
            case $map['trade_type'] == 3:
                // 余额
                // $data = [
                //
                // ];
                // $result = true;
                break;
            default:
                throw new ApiException('支付类型异常', 200, config('code.error'));
        }

        if ($result)
        {
            printf('单号【' . $map['refund_id'] . '】退款发起成功->修改退款订单状态:' . date('Y-m-d H:i:s') . PHP_EOL);
            (new LanmeiOrderRefund())->save(['status' => 2, 'update_time' => $time], ['id' => $map['id']]);
            return true;
        } else
        {
            return false;
        }
    }

}
